
package net.biaji.android.cmwrap.services;

import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;

import net.biaji.android.cmwrap.Config;
import net.biaji.android.cmwrap.Logger;
import net.biaji.android.cmwrap.utils.Utils;

import org.apache.commons.io.input.Tailer;
import org.apache.commons.io.input.TailerListener;
import org.apache.commons.io.input.TailerListenerAdapter;

public class NormalTcpServer implements WrapServer {

    private ServerSocket serSocket;
    Thread readNFlog;
    Tailer tailer;

    private final int servPort = 7443;

//    private String target;

    private String name;

    private String proxyHost;

    private int proxyPort;

    private final String TAG = "TCPServer";

    private boolean inService = false;

    private ExecutorService serv = Executors.newCachedThreadPool();
    
    Thread startProcess = null;

//    private static Hashtable<String, String> connReq = new Hashtable<String, String>();
    private static ConcurrentHashMap<String , Socket> socketReq = new ConcurrentHashMap<String, Socket>();

    private final String[] iptablesRules = new String[] {
    		//this rules couldn't change order, otherwise will not work
    		//"iptables -t nat -%3$s OUTPUT -p tcp -s 192.168.42.0/23 -o lo -m multiport ! --destination-port 80 -j REDIRECT --to-ports 7443 ",
    		//"iptables -t nat -%3$s PREROUTING -p tcp -m multiport ! --destination-port 80 -j REDIRECT --to-ports 7443 ",
    		//this two rules let package from external network which not 80 force go into 7443
    		"iptables -t nat -%3$s OUTPUT %1$s -p tcp  -m multiport --destination-port 80,8080 -j DNAT  --to-destination %2$s",
            "iptables -t nat -%3$s OUTPUT %1$s -p tcp -m multiport ! --destination-port 80,8080,7443 -j NFLOG ",
            "iptables -t nat -%3$s OUTPUT %1$s -p tcp -m multiport ! --destination-port 80,8080,7443 -j DNAT  --to-destination 127.0.0.1:7443",
            
            

            "iptables -t nat -D POSTROUTING -j natctrl_nat_POSTROUTING ",
            "iptables -t nat -%3$s POSTROUTING  %1$s -p tcp -s 192.168.42.0/23 --dport 80 -j MASQUERADE ",
            
            "iptables -t nat -D PREROUTING -j oem_nat_pre ",
            "iptables -t nat -%3$s PREROUTING -p tcp  --dport 80  -s 192.168.42.0/23 -j DNAT  --to-destination  %2$s ",//rule for share network from wan, usb...
            "iptables -t nat -%3$s PREROUTING -p tcp ! --dport 80 -s 192.168.42.0/23  -j NFLOG ", //rule for share network from wan, usb...
            "iptables -t nat -%3$s PREROUTING -p tcp ! --dport 80 -s 192.168.42.0/23 -j REDIRECT --to-ports 7443 ",//rule for share network from wan, usb...
            
    };
/**
 * 
iptables -t nat -A PREROUTING -i rndis0  -p tcp  --dport 80  -j DNAT  --to-destination 10.0.0.172:80
iptables -t nat -A PREROUTING -i rndis0  -p tcp -m multiport ! --destination-port 53,80,7442,7443,8000 -j NFLOG  
 iptables -t nat -I PREROUTING -p tcp --dport 443 -j NFLOG
iptables -t nat -A PREROUTING -i rndis0  -p tcp -m multiport ! --destination-port 53,80,7442,7443,8000 -j DNAT  --to-destination 127.0.0.1:7443 

 */
//
    public NormalTcpServer(String name) {
        this(name, "10.0.0.172", 80);
    }

    /**
     * @param name
     *            服务名称
     * @param proxyHost
     *            HTTP代理服务器地址
     * @param proxyPort
     *            HTTP代理服务器端口
     */
    public NormalTcpServer(String name, String proxyHost, int proxyPort) {
        this.name = name;
        this.proxyHost = proxyHost;
        this.proxyPort = proxyPort;
        try {
            serSocket = new ServerSocket();
            serSocket.setReuseAddress(true);
            serSocket.bind(new InetSocketAddress(servPort));
            inService = true;
        } catch (IOException e) {
            Logger.e(TAG, "Server初始化错误，端口号" + servPort, e);
        }
        startNFlogProcess();
        readNflogOutput();
        Logger.d(TAG, name + "启动于端口： " + servPort);

    }

    public boolean isClosed() {
        return (serSocket != null && serSocket.isClosed());
    }

    public void close() {
        inService = false;
        serv.shutdownNow();

        if (serSocket == null) {
            return;
        }

        try {
            serSocket.close();
        } catch (IOException e) {
            Logger.e(TAG, "", e);
        }
        tailer.stop();
        if (readNFlog != null)
        	readNFlog.interrupt();
		Utils.rootCMD("ps |grep connTracker|busybox awk '{print $2}'|busybox xargs kill -9");
		if(startProcess != null)
			startProcess.interrupt();
    }

    public int getServPort() {
        return servPort;
    }
    
    public void setProxyHost(String proxyHost) {
        this.proxyHost = proxyHost;

    }

    public void setProxyPort(int proxyPort) {
        this.proxyPort = proxyPort;

    }

    public void run() {

        if (serSocket == null) {
            Logger.d(TAG, "Server socket NOT initilized yet.");
            return;
        }
        
        while (inService) {
            try {
//                Logger.v(TAG, "等待客户端请求……");
                Socket socket = serSocket.accept();
                socket.setSoTimeout(120 * 1000);
//                Logger.v(TAG, "获得客户端请求");
                

                String srcPort = socket.getPort() + "";
                Logger.d(TAG, "NFlog coming request: " + socket.getInetAddress() +"->" + srcPort);
                socketReq.put(srcPort, socket);
            } catch (IOException e) {
                Logger.e(TAG, "伺服客户请求失败" + e.getMessage());
            } catch (RejectedExecutionException e) {
                Logger.e(TAG, "伺服客户请求失败" + e.getMessage());
            }
        }

        try {
            serSocket.close();
        } catch (IOException e) {
            Logger.e(TAG, name + "关闭服务端口失败：" + e.getMessage());
        }
        Logger.v(TAG, name + "侦听服务停止");

    }
    
    private void startNFlogProcess() {
		Utils.rootCMD("ps |grep connTracker|busybox awk '{print $2}'|busybox xargs kill -9");
		startProcess=new Thread(new Runnable(){

			@Override
			public void run() {
//				int result = -1;
				Process process = null;
				DataOutputStream os = null;
//				InputStream out = null;
				try {
								
		            process = new ProcessBuilder().command("su").redirectErrorStream(true).start();

//		            out = process.getInputStream();
//		            BufferedReader bro = new BufferedReader(new InputStreamReader(out), 1024 * 8);

		            os = new DataOutputStream(process.getOutputStream());

		            os.writeBytes(Config.parentPath +  "/connTracker \n");
		            os.flush();
		            
		            process.waitFor();
		            os.close();
		            
		        } catch (IOException e) {
		            Logger.e(TAG, "Failed to exec command", e);
		        } catch (InterruptedException e) {
		            Logger.e(TAG, "线程意外终止", e);
		        } finally {
		        	process.destroy();
		            try {
		                if (os != null) {
		                    os.close();
		                }
		                if (process != null)
		                    ;
		            } catch (IOException e) {
		            }

		        }
				
			}}, "startNFLogProcess");
		startProcess.start();
    }
    
	/**
	 * read connection information from connTracker process 
	 */
	private void readNflogOutput() {
		File pcounter_log = new File("/sdcard/connTracker");

		try {
			TailerListener listener = new IPTailListener();
			tailer = new Tailer(pcounter_log, listener, 10, true);

			readNFlog = new Thread(tailer, "readNFLogOutput");
			readNFlog.setPriority(Thread.MAX_PRIORITY);
			readNFlog.start();
		} catch (Exception e) {
			System.out.println(e);
		}
	
	}

	public class IPTailListener extends TailerListenerAdapter {
		public void handle(String line) {
			String[] parmArr = line.split("->");
//			Logger.d(TAG, "NFlog read iptables info:" + line);
			
			Socket socket = socketReq.get(ltrim(parmArr[0]));
			if(socket != null) {
				Logger.d(TAG, parmArr[0] +" 处理请求:" + socket.getInetAddress() +":"+socket.getPort()+"->" +parmArr[1]);
				serv.execute(new WapChannel(socket, parmArr[1], proxyHost, proxyPort));
			} 
			socketReq.remove(parmArr[0]);
		}
	}
	
	public static String ltrim(String s) {
	    int i = 0;
	    while (i < s.length() && 
	    		(Character.isWhitespace(s.charAt(i)) || (Character.getNumericValue(s.charAt(i)) == -1))
	    		) {
	        i++;
	    }
	    return s.substring(i);
	}


    public String[] getRules() {
        return iptablesRules;
    }

	@Override
	public void setTarget(String target) {
		// TODO Auto-generated method stub
		
	}

}
